//
// CPU.h
//
// x86-specific CPU model.
//

#ifndef __CPU_H__
#define __CPU_H__

#include <setjmp.h>

#include "base/Processor.h"
#include "base/simtypes.h"
#include "util/Pool.h"

#include "interp.h"

class Checkpoint {
  public:
    NEW_DELETE_OPERATORS_H(Checkpoint,NoSuper);
    Checkpoint () {};
    ~Checkpoint () {};
    VA getPC () { 
        return InterpCtxt.IP; 
    }    
    x86Context InterpCtxt;
};

class CPU {
  public:
    NEW_DELETE_OPERATORS_H(CPU,NoSuper);

    CPU (Processor* P,
         Byte* stack);
    ~CPU ();

    void reset (VA startPC, VA returnPC, 
                int argc, Reg arg0, Reg arg1, Reg arg2, Reg arg3);
    bool fetchDecodeExecuteCommitInstruction ();
    void takeCheckpoint (Checkpoint *cp);
    void restoreCheckpoint (Checkpoint *cp);
    void copyCheckpoint(Checkpoint * fromCp, Checkpoint * toCp);        
    void resumeAfterCheckpoint (Checkpoint* cp);
    void revokeReservations (VA ea);

    Result MemoryRead (uint64* results,
                       Reg vAddr,
                       uint length,
                       uint signed_load);
    Result MemoryWrite (uint64 value, Reg vAddr, uint length);

    void init (int i, Byte* stack);
    
    VA getPC () { 
        return InterpCtxt.IP; 
    }
    VA getStackPointer () { 
        return InterpCtxt.GPR[ESP]; 
    }
    int getID () { 
        return P->getID(); 
    }
    void setPC (VA pc) { 
        InterpCtxt.IP = pc; 
    }

    void lockMemory() { 
        memlock = true; 
    }
    void unlockMemory() {
        memlock = false;
    }

    void clearInstructionState () {
        unlockMemory();
    };

    void printHeartbeat_extra ();
    bool repInst ();

    void goto_real ();
    void setupStack (Byte* stack,
                     Reg startAddress,
                     Reg returnAddress,
                     int argc,
                     Reg arg0,
                     Reg arg1,
                     Reg arg2,
                     Reg arg3);

    bool mode64bit () { 
        return false; 
    }

    void printInstruction (Logger* log, 
                           SEVERITY severity, 
                           bool print_registers,
                           bool print_symbols);

    static void PrintInstruction (Logger* log, 
                                  SEVERITY severity, 
                                  CPU* cpu,
                                  bool print_registers,
                                  bool print_symbols);

    IVAL getEFlags(); 
    void setEFlags(IVAL eflags);


    // if true, update newIP at end of cycle.
    // if false, stall at existing curIP
    bool instrs_executed;

    x86Context InterpCtxt;
    // temporaries used during processing of an instruction
    struct temporaries {
        IVAL newIP;
        UINT segOverride;
        UINT fOpSize;
        UINT fAddrSize;
        UINT prefixes;
        UINT mod;
        UINT bits543;
        UINT rm;
        UINT sib;
        IVAL va;
        IVAL imm0;
        IVAL imm1;
    } tmp;

    jmp_buf    memoryJumpEnv;

    Processor* P;

    // gdb
    bool getRegister(int regnum, size_t len, uint8* val);
    bool putRegister(int regnum, size_t len, uint8* val);

    void resumeAtCheckpoint (Checkpoint *cp);
    void decrementPCInCheckpoint (Checkpoint *cp);

#ifdef WITH_PIN_SUPPORT    
    PINSTRFUNC checkSSE(UINT* ofs);
#endif // WITH_PIN_SUPPORT

  private:

    // remember where the initial stack pointer is, so we can restore it
    Byte* stack_aligned_end;

    bool memlock;
};

extern CPU* curPE;

#endif // __CPU_H__
